# -*- coding: utf-8 -*-
import re
import copy
from deepdiff import DeepDiff


class ModifyCaseData:
    """
    对新旧测试用例数据进行merge
    """
    def difference_node(self, dic1, dic2, node_list):
        """
        遍历字典，找出dic1中有，而dic2中没有的节点
        :param dic1: 遍历字典
        :param dic2: 对比字典，需要转换成str类型作为入参
        :param node_list: 定义的空列表
        :return: 返回dic1中有而dic2中没有的节点集合列表
        """
        if isinstance(dic1, dict):
            for k in dic1:
                if k == 'root':
                    self.difference_node(dic1['root'], dic2, node_list)
                elif k == 'data':
                    if dic1[k]['id'] not in str(dic2):
                        node_list.append(dic1)
                elif k == 'children':
                    self.difference_node(dic1['children'], dic2, node_list)
        elif isinstance(dic1, list):
            for j in dic1:
                self.difference_node(j, dic2, node_list)
        return node_list

    def unique_node(self, dic1, dic2):
        """
        遍历字典，找出dic1中有，而dic2中没有的节点，并对差异节点进行去重
        :param dic1: 遍历字典
        :param dic2: 对比字典
        :return: 返回dic1中有而dic2中没有的节点集合列表
        """
        node_list = self.difference_node(dic1, dic2, [])
        copy_list = copy.deepcopy(node_list)
        for i in node_list:
            for j in copy_list:
                str_i = str(i)
                str_j = str(j)
                if str_i in str_j and str_i != str_j:
                    copy_list.remove(i)
        return copy_list

    def father_node(self, dic, node, father_info):
        """
        遍历字典，根据节点ID，找出父节点信息
        :param dic: 差异节点所在的字典
        :param node: 差异节点
        :param father_info: 传空列表即可
        :return: 差异节点在原字典中父节点的ID和所在children内的索引信息列表
        """
        # 根据节点数据找到父节点
        if isinstance(dic, dict):
            for key in dic:
                if key == 'root':
                    self.father_node(dic['root'], node, father_info)
                elif key == 'children':
                    for k in dic[key]:
                        if k == node:
                            father_id = dic['data']['id']
                            father_index = dic[key].index(node)
                            father_info.append(father_id)
                            father_info.append(father_index)
                        else:
                            self.father_node(k, node, father_info)
        return father_info

    def father_node_info(self, dic1, dic2):
        """
        在父节点信息中插入差异子节点数据
        :param dic1: 遍历字典
        :param dic2: 对比字典
        :return: 返回差异节点信息，原字典中父节点的ID、所在children内的索引和节点数据信息列表
        """
        info_list = []
        node_list = self.unique_node(dic1, dic2)
        for node in node_list:
            info = self.father_node(dic1, node, [])
            info.append(node)
            info_list.append(info)
        return info_list

    def insert_node(self, dic, father_node_id, index, node_data):
        """
        遍历字典，找出目标节点数据到父节点，并将节点数据插入到父节点到指定位置
        :param dic: 遍历目标字典
        :param father_node_id: 父节点到ID
        :param index: 目标节点位置
        :param node_data: 目标节点数据
        :return:
        """
        if isinstance(dic, dict):
            if dic['data']['id'] == father_node_id:
                dic['children'].insert(index, node_data)
            else:
                if dic['children']:
                    dic = dic['children']
                    self.insert_node(dic, father_node_id, index, node_data)
        elif isinstance(dic, list):
            for i in dic:
                dic = i
                self.insert_node(dic, father_node_id, index, node_data)

    def update_dic(self, dic1, dic2, node_list):
        """
        更新新旧字典，使新旧字典节点数据一致
        :param dic1: 字典
        :param dic2: 字典
        :param node_list: 字典
        :return: 差异节点数据
        """
        dic1_info = self.father_node_info(dic1, dic2)
        for info in dic1_info:
            father_node_id = info[0]
            index = info[1]
            node_data = info[2]
            self.insert_node(dic2['root'], father_node_id, index, node_data)
            node_list.append(node_data)
        return node_list

    def node_index(self, dic, index_list):
        """
        遍历字典，找出字典中children节点里面子节点的index
        :param dic: 字典
        :param index_list: 传空列表即可
        :return: 输出列表，[[father_id,[[child_id,child_index],[child_id,child_index],...]],
                          [father_id,[[child_id,child_index],[child_id,child_index],...]],...]
        """
        if isinstance(dic, dict):
            father_id = dic['data']['id']
            if dic['children']:
                for child in dic['children']:
                    child_id = child['data']['id']
                    child_index = dic['children'].index(child)
                    child_info = [child_id, child_index]
                    node_info = [father_id, child_info]
                    index_list.append(node_info)
                    self.node_index(child, index_list)
            else:
                child_info = []
                node_info = [father_id, child_info]
                index_list.append(node_info)
        return index_list

    def update_index(self, dic, index_list):
        """
        更新字典的子节点位置顺序，使子节点的位置顺序与传入的index_list一致
        :param dic: 需更新的字典
        :param index_list: 需更新的子节点位置信息
        :return:
        """
        if isinstance(dic, dict):
            node_id = dic['data']['id']
            if dic['children']:
                for info in index_list:
                    if node_id == info[0]:
                        if info[1]:
                            # 原节点在children的位置
                            for child_node in dic['children']:
                                if child_node['data']['id'] == info[1][0]:
                                    old_index = dic['children'].index(child_node)
                                    # 新位置
                                    new_index = info[1][1]
                                    # 交换子节点位置
                                    dic['children'][old_index], dic['children'][new_index] = dic['children'][new_index], dic['children'][old_index]
                                    index_list.remove(info)
                self.update_index(dic['children'], index_list)
        elif isinstance(dic, list):
            for child in dic:
                self.update_index(child, index_list)

    @staticmethod
    def get_deep_list(str_key):
        """
        diff 字典key深度字符串转换成深度list
        :param str_key: 深度字符串
        :return: 深度list
        """
        deep_key_str = str_key[4:]
        deep_key = re.split("[\\[\'\\]]", deep_key_str)
        deep_list = []
        for key in deep_key:
            if key:
                if key.isnumeric():
                    key = int(key)
                deep_list.append(key)
        return deep_list

    @staticmethod
    def find_value(walk_dict, deep_list):
        """
        根据key的深度列表查找值
        :param walk_dict: 目标数据
        :param deep_list: 目标key的深度列表
        :return: 返回目标值
        """
        for key in deep_list[:-1]:
            walk_dict = walk_dict[key]
        data = walk_dict[deep_list[-1]]
        return data

    @staticmethod
    def delete_value(walk_dict, deep_list):
        """
        根据key的深度列表查找值
        :param walk_dict: 目标数据
        :param deep_list: 目标key的深度列表
        :return: 返回目标值
        """
        for key in deep_list[:-1]:
            walk_dict = walk_dict[key]
        walk_dict.pop(deep_list[-1])

    @staticmethod
    def add_value(walk_dict, deep_list, value):
        """
        根据key的深度列表查找值
        :param walk_dict: 目标数据
        :param deep_list: 目标key的深度列表
        :param value: 添加的value
        :return: 返回目标值
        """
        for key in deep_list[:-1]:
            walk_dict = walk_dict[key]
        walk_dict[deep_list[-1]] = value

    @staticmethod
    def update_value(walk_dict, deep_list, new_value):
        """
        更新字典的值
        :param walk_dict: 遍历目标数据
        :param deep_list: 目标key的深度列表
        :param new_value: 更新的value
        :return:
        """
        for key in deep_list[:-1]:
            walk_dict = walk_dict[key]
        walk_dict[deep_list[-1]] = new_value

    def delete_node(self, dic, node_data):
        """
        根据节点ID删除字典中的节点
        """
        if isinstance(dic, dict):
            for key in dic:
                if isinstance(dic[key], dict):
                    self.delete_node(dic[key]['children'], node_data)
        if isinstance(dic, list):
            for node in dic:
                if node == node_data:
                    dic.remove(node)
                elif node['children']:
                    self.delete_node(node['children'], node_data)

    @staticmethod
    def merge_list(list1, list2):
        """
        将数组1和数组2进行合并，并去重
        :param list1: 数组1
        :param list2: 数组2
        :return: 返回合并的数组
        """
        for k in list1:
            if k not in list2:
                list2.append(k)
        return list2

    def merge_case(self, dic1, dic2, get_time, update_time):
        """
        将字典dic1和字典dic2更新成两个节点数量及顺序完全相同的字典
        :param dic1: 旧测试用例数据字典
        :param dic2: 新测试用例数据字典
        :param get_time: 获取用例的时间
        :param update_time: 提交用例的时间
        :return:
        """
        dic1 = eval(dic1)
        dic2 = eval(dic2)
        # 更新字典dic1，并输出旧测试用例数据中有，而新测试用例数据中没有的节点列表
        node_list1 = self.update_dic(dic1, dic2, [])
        # 更新字典dic2，并输出新测试用例数据中有，而旧测试用例数据中没有的节点列表
        node_list2 = self.update_dic(dic2, dic1, [])
        # 调整字典dic1的节点顺序，使之与dic2节点顺序一致
        lis = self.node_index(dic2['root'], [])
        self.update_index(dic1['root'], lis)

        # 找出新用例dic2中需要删除的节点
        delete_list1 = []
        delete_list2 = []
        # 旧用例中有，而新用例中没有 -- 如果节点时间戳小于get_time，说明是原始用例，被自己删除了，所以旧用例中有而新用例中没有，新用例需要删除
        for data1 in node_list1:
            if data1['data']['created'] < get_time:
                delete_list1.append(data1)
        # 新用例中有，而旧用例中没有 -- 如果节点时间戳小于get_time，说明是被旧用例删除了，所以新用例需要删除
        for data2 in node_list2:
            if data2['data']['created'] < get_time:
                delete_list2.append(data2)
        for i in delete_list1:
            if i not in delete_list2:
                delete_list2.append(i)

        # 删除旧用例数据字典中需要删除的节点
        for data in delete_list2:
            self.delete_node(dic1, data)
        # 删除新用例数据字典中需要删除的节点
        for data in delete_list2:
            self.delete_node(dic2, data)

        # 输出old和new两个用例数据中的差异点
        diff = DeepDiff(dic1, dic2)
        # 更新new中的数据
        for key in diff:
            if key == 'values_changed':
                for changed_key in diff[key]:
                    deep_list = self.get_deep_list(changed_key)
                    copy_list = copy.deepcopy(deep_list)
                    if len(deep_list) != 1:
                        if deep_list[-1] != 'created':
                            if isinstance(deep_list[-1], int):
                                deep_list = deep_list[:-2]
                                deep_list.append('created')
                            else:
                                if deep_list[-2] == 'resource':
                                    deep_list = deep_list[:-1]
                                deep_list[-1] = 'created'
                        old_timestamp = self.find_value(dic1, deep_list)
                        if old_timestamp > get_time:
                            # 如果old_timestamp大于get_time，则说明老用例被修改过，需要将新用例的值更新为老用例的值
                            old_value = diff[key][changed_key]['old_value']
                            # 更新新用例的节点值
                            self.update_value(dic2, copy_list, old_value)
                        # 更新新用例的节点时间戳
                        self.update_value(dic2, deep_list, update_time)
            elif key == 'iterable_item_added':
                for k, v in diff[key].items():
                    deep_list = self.get_deep_list(k)
                    copy_list = copy.deepcopy(deep_list)
                    if deep_list[-1] != 'created':
                        if isinstance(deep_list[-1], int):
                            if deep_list[-2] == 'children':
                                deep_list.append('data')
                                deep_list.append('created')
                            else:
                                deep_list = deep_list[:-2]
                                deep_list.append('created')
                        else:
                            if deep_list[-2] == 'resource':
                                deep_list = deep_list[:-1]
                                copy_list = copy_list[:-1]
                            deep_list[-1] = 'created'
                    old_timestamp = self.find_value(dic2, deep_list)
                    if old_timestamp > get_time:
                        # 如果old_timestamp大于get_time，则说明被老用例删掉了，新用例也需要将该值删除
                        new_value = self.find_value(dic2, copy_list)
                        data = new_value.remove(v)
                        self.update_value(dic2, copy_list, data)
                    # 更新新用例的节点时间戳
                    self.update_value(dic2, deep_list, update_time)
            elif key == 'iterable_item_removed':
                for k, v in diff[key].items():
                    deep_list = self.get_deep_list(k)
                    copy_list = copy.deepcopy(deep_list)
                    if deep_list[-1] != 'created':
                        if isinstance(deep_list[-1], int):
                            if deep_list[-2] == 'children':
                                deep_list = deep_list[:-2]
                                deep_list.append('data')
                                deep_list.append('created')
                            else:
                                deep_list = deep_list[:-2]
                                deep_list.append('created')
                        else:
                            if deep_list[-2] == 'resource':
                                deep_list = deep_list[:-1]
                                copy_list = copy_list[:-1]
                            deep_list[-1] = 'created'
                    old_timestamp = self.find_value(dic1, deep_list)
                    if old_timestamp > get_time:
                        # 如果old_timestamp大于get_time，则说明老用例增加了，新用例也需要将该值加上
                        # 先判断新用例有没有该key，如果没有，则加上
                        new_value = self.find_value(dic2, copy_list)
                        data = new_value.append(v)
                        self.update_value(dic2, copy_list, data)
                    # 更新新用例的节点时间戳
                    self.update_value(dic2, deep_list, update_time)
            elif key == 'dictionary_item_added':
                # 如果新用例中有而旧用例中没有，则使用旧用例的时间戳和获取用例的时间戳做比较
                for k in diff[key]:
                    deep_list = self.get_deep_list(k)
                    copy_list = copy.deepcopy(deep_list)
                    if deep_list[-1] != 'created':
                        if isinstance(deep_list[-1], int):
                            if deep_list[-2] == 'children':
                                deep_list.append('data')
                                deep_list.append('created')
                            else:
                                deep_list = deep_list[:-2]
                                deep_list.append('created')
                        else:
                            deep_list[-1] = 'created'
                    old_timestamp = self.find_value(dic2, deep_list)
                    if old_timestamp > get_time:
                        # 如果old_timestamp大于get_time，则说明老用例删除了该键值对，新用例需要将该键值对删除
                        self.delete_value(dic2, copy_list)
                    # 更新新用例的节点时间戳
                    self.update_value(dic2, deep_list, update_time)
            elif key == 'dictionary_item_removed':
                # 如果新用例中没有而旧用例中有，则使用旧用例的时间戳和获取用例的时间戳做比较
                for k in diff[key]:
                    deep_list = self.get_deep_list(k)
                    copy_list = copy.deepcopy(deep_list)
                    if deep_list[-1] != 'created':
                        if isinstance(deep_list[-1], int):
                            if deep_list[-2] == 'children':
                                deep_list = deep_list[:-2]
                                deep_list.append('data')
                                deep_list.append('created')
                            else:
                                deep_list = deep_list[:-2]
                                deep_list.append('created')
                        else:
                            deep_list[-1] = 'created'
                    old_timestamp = self.find_value(dic1, deep_list)
                    if old_timestamp > get_time:
                        # 如果old_timestamp大于get_time，则说明老用例增加了键值对，新用例也需要增加该键值对
                        data = self.find_value(dic1, copy_list)
                        self.add_value(dic2, copy_list, data)
                    # 更新新用例的节点时间戳
                    self.update_value(dic2, deep_list, update_time)
        return str(dic2)
